package cn.micro.core.retry;


import cn.micro.core.util.Exceptions;
import com.github.rholder.retry.*;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * 简单的 retry 重试
 */
public final class SimpleRetry implements IRetry {
    /**
     * The default limit to the number of attempts for a new policy.
     */
    public static final int DEFAULT_MAX_ATTEMPTS = 3;
    /**
     * Default back off period - 1ms.
     */
    private static final long DEFAULT_BACK_OFF_PERIOD = 1L;

    /**
     * 重试次数
     */
    private final int maxAttempts;
    /**
     * 重试时间间隔
     */
    private final long sleepMillis;

    public SimpleRetry() {
        this(DEFAULT_MAX_ATTEMPTS, DEFAULT_BACK_OFF_PERIOD);
    }

    public SimpleRetry(int maxAttempts) {
        this(maxAttempts, DEFAULT_BACK_OFF_PERIOD);
    }

    public SimpleRetry(int maxAttempts, long sleepMillis) {
        this.maxAttempts = maxAttempts;
        this.sleepMillis = (sleepMillis > 0 ? sleepMillis : 1);
    }

    public int getMaxAttempts() {
        return maxAttempts;
    }

    public long getSleepMillis() {
        return sleepMillis;
    }

    @Override
    public <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback) throws E {
        Retryer<T> retryer = RetryerBuilder.<T>newBuilder()
                .retryIfException()
                .withStopStrategy(StopStrategies.stopAfterAttempt(maxAttempts))
                .withWaitStrategy(WaitStrategies.fixedWait(sleepMillis, TimeUnit.MILLISECONDS))
                .build();
        try {
            return retryer.call(() -> {
                try {
                    return retryCallback.call();
                } catch (Throwable e) {
                    throw Exceptions.unchecked(e);
                }
            });
        } catch (ExecutionException | RetryException e) {
            throw Exceptions.unchecked(e);
        }
    }

}
